package com.joysuch.wwyt.workflow.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.reflect.TypeToken;
import com.joysuch.wwyt.alert.entity.NoticeWaitingTriggerList;
import com.joysuch.wwyt.alert.enums.TriggerStatusEnum;
import com.joysuch.wwyt.alert.service.NoticeWaitingTriggerListService;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiCoreException;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.mh.bean.ProcessNoticeSearchBean;
import com.joysuch.wwyt.util.GsonUtils;
import com.joysuch.wwyt.workflow.bean.notice.*;
import com.joysuch.wwyt.workflow.entity.ProcessNoticeList;
import com.joysuch.wwyt.workflow.enums.ComponentSelectTypeEnum;
import com.joysuch.wwyt.workflow.enums.NoticeComponentEnum;
import com.joysuch.wwyt.workflow.mapper.ProcessNoticeListMapper;
import com.joysuch.wwyt.workflow.service.ProcessNoticeListService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author jwk
 * @since 2021-07-20
 */
@Service
@Slf4j
public class ProcessNoticeListServiceImpl extends ServiceImpl<ProcessNoticeListMapper, ProcessNoticeList> implements ProcessNoticeListService {

    @Autowired
    private NoticeWaitingTriggerListService noticeWaitingTriggerListService;

    @Override
    public IPage<ProcessNoticeList> findListPaging(ProcessNoticeSearchBean searchBean) {
        Page<ProcessNoticeList> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        LambdaQueryWrapper<ProcessNoticeList> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProcessNoticeList::getDeleteFlag, "0");
        wrapper.eq(ProcessNoticeList::getProcessId, searchBean.getProcessId());
        wrapper.like(StringUtils.isNotBlank(searchBean.getName()), ProcessNoticeList::getNoticeName, searchBean.getName());
        wrapper.eq(ProcessNoticeList::getTenentId, Context.getCurrentTenantId());
        wrapper.likeRight(ProcessNoticeList::getOrgCode, Context.getCompanyCode());
        return super.page(page, wrapper);
    }

    @Override
    public boolean generateNotice(final Long processId, final String taskCode, final String processName, final String formData, final String objectId) {
        List<ProcessNoticeList> noticeList = findAllProcessNoticeList(processId);
        List<NoticeWaitingTriggerList> triggerList = findTriggerList(taskCode);
        JSONObject fromJson = JSON.parseObject(formData);
        List<Date> waitingTriggerDates = triggerList.stream().map(NoticeWaitingTriggerList::getOperationTime).collect(Collectors.toList());
        for (ProcessNoticeList each : noticeList) {
            try {
                NoticeTriggerConditions conditions = GsonUtils.fromJson(each.getTriggerConditions(), NoticeTriggerConditions.class);
                DelayCondition condition = conditions.getDelayCondition();
                String delayMinute = transformTrigger(each.getTriggerRule());
                String key = condition.getKey();
                Object valueJsonObject = fromJson.get(key);
                if (valueJsonObject != null) {
                    String label = condition.getLabel();
                    List<TransformedUserGroup> userGroupTfs = transformedUserGroup(each.getNoticeUserGroup(), fromJson, objectId);
                    if (CollectionUtils.isNotEmpty(userGroupTfs)) {
                        if (JSONUtil.isJsonArray(valueJsonObject.toString())) {
                            JSONArray jsonArray = JSON.parseArray(valueJsonObject.toString());
                            for (Object json : jsonArray) {
                                if (JSONUtil.isJsonObj(json.toString())) {
                                    JSONObject jsonObject = JSON.parseObject(json.toString());
                                    Date operationTime = parseOperationTime(label, jsonObject);
                                    if (operationTime != null && !waitingTriggerDates.contains(operationTime)) {
                                        String noticeContent = transformedContent(formData, jsonObject, each.getNoticeContent(), each.getNoticeContentVal());
                                        addNoticeToWaitingList(taskCode, each.getId(), delayMinute, GsonUtils.toJson(userGroupTfs), operationTime, processName, noticeContent);
                                    }
                                }
                            }
                        } else {
                            //单个组件
                            Date operationTime = DateUtil.parse(valueJsonObject.toString());
                            if (operationTime != null && !waitingTriggerDates.contains(operationTime)) {
                                String noticeContent = transformedContent(formData, null, each.getNoticeContent(), each.getNoticeContentVal());
                                addNoticeToWaitingList(taskCode, each.getId(), delayMinute, GsonUtils.toJson(userGroupTfs), operationTime, processName, noticeContent);
                            }
                        }
                    }
                }
            } catch (Throwable e) {
                log.error("Task generate notice error,task code {} ", taskCode, e);
            }
        }
        return false;
    }

    @Override
    public List<NoticeWaitingTriggerList> findTriggerList(String taskCode) {
        LambdaQueryWrapper<NoticeWaitingTriggerList> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(NoticeWaitingTriggerList::getDeleteFlag, "0");
        wrapper.eq(NoticeWaitingTriggerList::getTaskCode, taskCode);
        return noticeWaitingTriggerListService.list(wrapper);
    }

    private String transformedContent(final String formData, final JSONObject jsonObject, final String noticeContent, final String noticeContentVal) {
        List<NoticeContent> valList = GsonUtils.fromJson(noticeContentVal, new TypeToken<List<NoticeContent>>() {
        }.getType());
        String transformedContent = noticeContent;
        for (NoticeContent val : valList) {
            JSONObject fromJson = JSON.parseObject(formData);
            Object contentValue = fromJson.get(val.getKey());
            if (contentValue != null) {
                final String contentValueStr = contentValue.toString();
                if (NoticeComponentEnum.TABLE.getValue().equals(val.getType())) {
                    if (ComponentSelectTypeEnum.MULTIPLE_CHOICE.getValue().equals(val.getSelectType())) {
                        contentValue = parseMultipleChoiceContent(val.getLabel(), contentValueStr);
                    } else {
                        //处理发送对象和条件组件是否在同一表单内的组件
                        if (jsonObject != null) {
                            Object value = jsonObject.get(val.getLabel());
                            if (value == null || Constant.EMPTY_STR.equals(value)) {
                                contentValue = parseArrayContent(val.getLabel(), contentValueStr);
                            } else {
                                contentValue = jsonObject.get(val.getLabel());
                            }
                        } else {
                            contentValue = parseArrayContent(val.getLabel(), contentValueStr);
                        }
                    }
                } else {
                    if (ComponentSelectTypeEnum.MULTIPLE_CHOICE.getValue().equals(val.getSelectType())) {
                        contentValue = parseMultipleChoiceArr(new StringBuilder(), contentValueStr);
                    }
                }
            }
            transformedContent = transformedContent.replace(String.format("${%s}", val.getLabel()), contentValue != null ? contentValue.toString() : Constant.EMPTY_STR);
        }
        return transformedContent;
    }

    private String parseMultipleChoiceArr(StringBuilder valueBuilder, final String contentValue) {
        if (JSONUtil.isJsonArray(contentValue)) {
            JSONArray multipleChoiceArray = JSON.parseArray(contentValue);
            for (Object item : multipleChoiceArray) {
                JSONObject itemJson = JSON.parseObject(item.toString());
                Object labelVal = itemJson.get(Constant.FROM_DATA_LABEL_KEY);
                if (labelVal != null) {
                    if (valueBuilder.length() > 0) {
                        valueBuilder.append(Constant.SPLIT_COMMA);
                    }
                    valueBuilder.append(labelVal);
                }
            }
            return valueBuilder.toString();
        }
        return Constant.EMPTY_STR;
    }

    private String parseArrayContent(final String valLabel, final String contentValue) {
        if (JSONUtil.isJsonArray(contentValue)) {
            StringBuilder valueBuilder = new StringBuilder();
            JSONArray multipleChoiceArray = JSON.parseArray(contentValue);
            for (Object item : multipleChoiceArray) {
                JSONObject itemJson = JSON.parseObject(item.toString());
                Object labelVal = itemJson.get(valLabel);
                if (labelVal != null) {
                    if (valueBuilder.length() > 0) {
                        valueBuilder.append(Constant.SPLIT_COMMA);
                    }
                    valueBuilder.append(labelVal);
                }
            }
            return valueBuilder.toString();
        }
        return Constant.EMPTY_STR;
    }

    private String parseMultipleChoiceContent(final String valLabel, final String contentValue) {
        if (JSONUtil.isJsonArray(contentValue)) {
            StringBuilder valueBuilder = new StringBuilder();
            JSONArray multipleChoiceArray = JSON.parseArray(contentValue);
            for (Object item : multipleChoiceArray) {
                JSONObject itemJson = JSON.parseObject(item.toString());
                Object labelVal = itemJson.get(valLabel);
                if (labelVal != null) {
                    parseMultipleChoiceArr(valueBuilder, labelVal.toString());
                }
            }
            return valueBuilder.toString();
        }
        return Constant.EMPTY_STR;
    }

    private List<TransformedUserGroup> transformedUserGroup(final String noticeUserGroup, final JSONObject fromJson, final String objectId) throws ApiCoreException {
        List<NoticeUserGroup> userGroupList = GsonUtils.fromJson(noticeUserGroup, new TypeToken<List<NoticeUserGroup>>() {
        }.getType());
        List<TransformedUserGroup> userGroupTfs = new ArrayList<>();
        for (NoticeUserGroup group : userGroupList) {
            String userGroupKey = group.getKey();
            String userGroupLabel = group.getLabel();
            String componentTypeVal = group.getComponentType();
            if (NoticeComponentEnum.find(componentTypeVal) == null) {
                throw new ApiCoreException(Status.NOTICE_COMPONENT_TYPE_NOT_EXIST);
            }
            //表单组件
            StringBuilder valueBuilder = null;
            NoticeComponentEnum componentType = NoticeComponentEnum.find(componentTypeVal);
            switch (componentType) {
                case TABLE:
                    String userGroupJsonStr = JSON.toJSONString(fromJson.get(userGroupKey));
                    if(StringUtils.isNotBlank(userGroupJsonStr) && !Constant.NULL_STR.equalsIgnoreCase(userGroupJsonStr)){
                        valueBuilder = builderTableUserGroupIdList(userGroupLabel, userGroupJsonStr);
                    }
                    break;
                case SELECT:
                    JSONObject objectIdJson = JSON.parseObject(objectId);
                    Object oneObjectId = objectIdJson.get(userGroupKey);
                    if (oneObjectId != null) {
                        valueBuilder = builderSelectUserGroupIdList(oneObjectId.toString());
                    }
                    break;
                case PERSON:
                case ROLE:
                    valueBuilder = new StringBuilder().append(userGroupKey);
                    break;
            }
            if (valueBuilder != null && valueBuilder.length() > 0) {
                TransformedUserGroup transformedUserGroup = new TransformedUserGroup();
                transformedUserGroup.setKey(group.getType());
                transformedUserGroup.setValue(valueBuilder.toString());
                userGroupTfs.add(transformedUserGroup);
            }
        }
        return userGroupTfs;
    }

    private StringBuilder builderSelectUserGroupIdList(String oneObjectId) {
        StringBuilder valueBuilder = new StringBuilder();
        JSONArray userGroupIdArray = JSON.parseArray(oneObjectId);
        for (Object idJson : userGroupIdArray) {
            if (valueBuilder.length() > 0) {
                valueBuilder.append(Constant.SPLIT_COMMA);
            }
            JSONObject idJsonGroup = JSON.parseObject(idJson.toString());
            Object value = idJsonGroup.get(Constant.FROM_DATA_ID_KEY);
            if (value != null) {
                valueBuilder.append(value);
            }
        }
        return valueBuilder;
    }

    private StringBuilder builderTableUserGroupIdList(final String userGroupLabel, final String userGroupJsonStr) throws ApiCoreException {
        StringBuilder valueBuilder = new StringBuilder();
        JSONArray userGroupArray = JSON.parseArray(userGroupJsonStr);
        for (Object json : userGroupArray) {
            JSONObject userGroupJson = JSON.parseObject(json.toString());
            if (userGroupJson != null && userGroupJson.size() > 0) {
                Object userGroupIdObj = userGroupJson.get(userGroupLabel.concat(Constant.FROM_DATA_ID_KEY));
                if (userGroupIdObj == null) {
                    log.error("userGroupJsonStr parse error,{}", userGroupJsonStr);
                    throw new ApiCoreException(Status.NOTICE_COMPONENT_DATA_ERROR);
                }
                String userGroupIdStr = userGroupIdObj.toString();
                JSONArray userGroupIdArray = JSON.parseArray(userGroupIdStr);
                if (JSONUtil.isJsonArray(userGroupIdStr)) {
                    for (Object idJson : userGroupIdArray) {
                        if (valueBuilder.length() > 0) {
                            valueBuilder.append(Constant.SPLIT_COMMA);
                        }
                        JSONObject idJsonGroup = JSON.parseObject(idJson.toString());
                        Object value = idJsonGroup.get(Constant.FROM_DATA_ID_KEY);
                        if (value != null) {
                            valueBuilder.append(value);
                        }
                    }
                }
            }
        }
        return valueBuilder;
    }

    private String transformTrigger(final String triggerRuleStr) {
        TriggerRule triggerRule = GsonUtils.fromJson(triggerRuleStr, TriggerRule.class);
        return triggerRule.getDelayTime();
    }

    private void addNoticeToWaitingList(String taskCode, Long noticeId, String delayMinutes, String userGroupTfs, Date operationTime, String noticeTitle, String noticeContent) {
        Date sendTime = calculateSendTime(operationTime, delayMinutes);
        NoticeWaitingTriggerList noticeWaitingTriggerList = new NoticeWaitingTriggerList();
        noticeWaitingTriggerList.setNoticeId(noticeId);
        noticeWaitingTriggerList.setNoticeUserGroup(userGroupTfs);
        noticeWaitingTriggerList.setSendTime(sendTime);
        noticeWaitingTriggerList.setNoticeContent(noticeContent);
        noticeWaitingTriggerList.setOperationTime(operationTime);
        noticeWaitingTriggerList.setTaskCode(taskCode);
        noticeWaitingTriggerList.setNoticeTitle(noticeTitle);
        noticeWaitingTriggerList.setTriggerStatus(TriggerStatusEnum.NOT_TRIGGERED.getStatus());
        noticeWaitingTriggerListService.save(noticeWaitingTriggerList);
    }

    @Override
    public List<ProcessNoticeList> findAllProcessNoticeList(final Long processId) {
        LambdaQueryWrapper<ProcessNoticeList> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProcessNoticeList::getDeleteFlag, "0");
        wrapper.eq(ProcessNoticeList::getProcessId, processId);
        wrapper.eq(ProcessNoticeList::getTenentId, Context.getCurrentTenantId());
        wrapper.likeRight(ProcessNoticeList::getOrgCode, Context.getCompanyCode());
        return super.list(wrapper);
    }

    private List<Date> parseOperationTime(final String label, final String valueJsonString) throws ApiCoreException {
        List<Date> dateList = new ArrayList<>();
        String operationTimeStr;
        //表单组件
        if (JSONUtil.isJsonArray(valueJsonString)) {
            JSONArray jsonArray = JSON.parseArray(valueJsonString);
            for (Object json : jsonArray) {
                if (JSONUtil.isJsonObj(json.toString())) {
                    JSONObject jsonObject = JSON.parseObject(json.toString());
                    if (jsonObject != null && jsonObject.size() > 0) {
                        if (jsonObject.get(label) != null) {
                            operationTimeStr = jsonObject.get(label).toString();
                            Date operationTime = DateUtil.parse(operationTimeStr);
                            dateList.add(operationTime);
                        } else {
                            log.error("from data valueJsonStr label not find,valueJsonStr: {},label: {}", valueJsonString, label);
                            throw new ApiCoreException(Status.FROM_DATA_LABEL_NOT_EXIST);
                        }
                    }
                }
            }
        } else {
            //单个组件
            Date operationTime = DateUtil.parse(valueJsonString);
            dateList.add(operationTime);
        }
        return dateList;
    }

    private Date parseOperationTime(final String label, final JSONObject jsonObject) throws ApiCoreException {
        //表单组件
        String operationTimeStr;
        if (jsonObject != null && jsonObject.size() > 0) {
            if (jsonObject.get(label) != null) {
                operationTimeStr = jsonObject.get(label).toString();
                return DateUtil.parse(operationTimeStr);
            } else {
                log.error("from data jsonObject label not find,jsonObject: {},label: {}", jsonObject, label);
                throw new ApiCoreException(Status.FROM_DATA_LABEL_NOT_EXIST);
            }
        }
        return null;
    }


    private Date calculateSendTime(final Date operationTime, final String delayTime) {
        return DateUtil.offsetMinute(operationTime, Integer.parseInt(delayTime));
    }

}
